

package com.timber.util;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Iterator;
import java.util.HashSet;

/**
 * Arrays utilities.
 */
public abstract class ArrayUtil {
    /**
     * Implements collection interface of ordered list of aggregated elements.
     * Each element in this list has unique hashCode. If hashCode of new added
     * element collides with hashCode of any already added element, these two
     * elements are merged together.
     */
    public final static class AggregatedList implements Collection {
        /**
         * All elements of aggregated list should impelemt this interface.
         */
        public static interface Element {
            /**
             * This method is called in case of hashCode collision. An element
             * should implement this method to merge itself with another object
             * with the same hashCode.
             */
            public void mergeWith(Object object);
        }

        private LinkedHashMap map = new LinkedHashMap(100);

        public int size() {
            return map.size();
        }

        public boolean isEmpty() {
            return map.isEmpty();
        }

        public boolean contains(Object o) {
            return map.values().contains(o);
        }

        public Iterator iterator() {
            return map.values().iterator();
        }

        public Object[] toArray() {
            return map.values().toArray();
        }

        public Object[] toArray(Object a[]) {
            return map.values().toArray(a);
        }

        public boolean add(Object o) {
            if (!(o instanceof AggregatedList.Element)) {
                throw new IllegalArgumentException();
            }
            Object key = new Integer(o.hashCode());
            AggregatedList.Element element = (AggregatedList.Element) map.get(key);
            if (element != null) {
                element.mergeWith(o);
            } else {
                map.put(key, o);
            }
            return true;
        }

        public boolean remove(Object o) {
            return (map.remove(new Integer(o.hashCode())) != null);
        }

        public boolean containsAll(Collection c) {
            return map.values().containsAll(c);
        }

        public boolean addAll(Collection c) {
            for (Iterator j = c.iterator(); j.hasNext(); ) {
                add(j.next());
            }
            return true;
        }

        public boolean removeAll(Collection c) {
            boolean result = false;
            for (Iterator j = c.iterator(); j.hasNext(); ) {
                result |= remove(j.next());
            }
            return result;
        }

        public boolean retainAll(Collection c) {
            throw new UnsupportedOperationException();
        }

        public void clear() {
            map.clear();
        }
    }

    /**
     * Accumulates and prints a collection of unique objects.
     */
    public final static class Uniques extends HashSet {
        private final String emptyValue;

        /**
         * @param emptyValue The value returned is the set it empty.
         */
        public Uniques(String emptyValue) {
            this.emptyValue = emptyValue;
        }

        /**
         * Prints comma-separated list of values it stores.
         */
        public String toString() {
            StringBuffer sb = new StringBuffer();
            Object o;
            for (Iterator i = iterator(); i.hasNext();) {
                if ((o = i.next()) != null) {
                    sb.append(o.toString());
                    if (i.hasNext()) {
                        sb.append(",");
                    }
                }
            }
            return sb.length()>0 ? sb.toString() : emptyValue;
        }
    }
}
